package com.example.demo.aspectLogic;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Aspect
@Component
public class MyAspect {
    private static final Logger LOG = LoggerFactory.getLogger(MyAspect.class);

    /**
     * 定义切入点
     *
     * 表达式举例说明：execution(* com.example.demo..*.*(..))
     * 1) 执行 execution(xxx)	表达式的主体,xxx是表达式的规则;
     * 2) 第一个"*"符号	        表示返回值的类型任意; * 表示所有类型（就是方法的返回参数没有限制）， string 表示string类型（只有方法的返回参数是string才生效）
     * 3) com.example.demo	    AOP所切的包，就是在哪个包下生效，也可以用*（代表所有包，但是一般不会用*，不然一启动项目你就会产生大量的切面执行）
     * 4) 包名后面的".."	        表示当前包及子包
     * 5) 第二个"*"	            表示类名，*即所有类, *Controller2 表示类名后缀是Controller2的类
     * 6) .*(..)	            表示任何方法名，括号表示参数，两个点表示任何参数类型, .*Test1(..)	表示方法名后缀是Test1的所有方法，参数无限制，
     *                          .*Test(*)	表示方法名后缀是Test1的所有方法，参数只能是一个的，
     *                          .*Test(*,*)	表示方法名后缀是Test1的所有方法，参数只能是两个的。
     */
    @Pointcut("execution(String com.example.demo.controller..*Asperct(..))")
    public void MyPointcut(){
    }

    /**
     * @Before 在目标方法被调用之前执行,定义可以用切入点方法,也可以自己用execution()表达式
     * @param joinPoint
     */
//    @Before("execution(* com.example.demo.controller..*Test(..))")
    @Before("MyPointcut()")
    public void doBefore(JoinPoint joinPoint){
        LOG.info("我在AOP层，我在方法之前执行");

        //获取参数值
        Object[] args = joinPoint.getArgs();
        if (args.length > 0){
            for (int i = 0; i < args.length; i++) {
                LOG.info("参数" + i + ":" + args[i]);
            }
        }
        LOG.info("类的路径：" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //request获取
        HttpServletRequest request = attributes.getRequest();
    }

    /**
     * @After 在目标方法完成之后执行,定义可以用切入点方法,也可以自己用execution()表达式
     * @param joinPoint
     */
    @After("MyPointcut()")
    public void doAfter(JoinPoint joinPoint){
        LOG.info("我在AOP层，我在方法之后执行");
    }

    /**
     * @AfterThrowing 在目标方法出现异常时执行,定义可以用切入点方法,也可以自己用execution()表达式
     * @param joinPoint
     */
    @AfterThrowing("MyPointcut()")
    public void doAfterThrowing(JoinPoint joinPoint){
        LOG.info("我在AOP层，我在异常时执行");
    }

    /**
     * @AfterReturning 在目标方法正常完成后执行,定义可以用切入点方法,也可以自己用execution()表达式
     * @param joinPoint
     */
    @AfterReturning("MyPointcut()")
    public void doAfterReturning(JoinPoint joinPoint){
        LOG.info("我在AOP层，我在返回一个结果后执行");
    }

    /**
     *
     * @param pJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("execution(String com.example.demo.controller.AspectController.testAsperctAround(..))")
    public Object  doAround(ProceedingJoinPoint pJoinPoint) throws Throwable {
        LOG.info("我在AOP层");
        LOG.info("我在AOP层，@Around 我在方法之前执行");
        Object[] args = pJoinPoint.getArgs();
        args[0] = "修改参数值";
        Object proceed = pJoinPoint.proceed(args);
        LOG.info("我在AOP层，@Around 我在方法之后执行");
        return proceed;
    }

}
